#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "imbus_internal.h"
#include "im_list.h"
#include "im_object_queue.h"

/************************** Declaration of internal functions ***************/
static void     __im_object_queue_initialize    (IMPointer      queue);
static void     __im_object_queue_finalize      (IMPointer      queue);
static void     __im_object_queue_copy          (IMPointer      dest,
                                                 IMConstPointer src);
static IMBool   __im_object_queue_serialize     (IMConstPointer obj,
                                                 IMPointer      stream);
static IMBool   __im_object_queue_deserialize   (IMPointer      obj,
                                                 IMPointer      stream);
static void     __im_object_queue_class_init    (IMPointer      klass);

/****************** Declaration of object and class structure ************/
struct _IMObjectQueue
{
    IMObject object;

    /**< private >**/
    IMList *head;
    IMList *tail;
    IMUInt  count;
};

struct _IMObjectQueueClass
{
    IMObjectClass parent_class;
};

/************************ Internal varable ************************/
static const IMObjectClass *__im_object_queue_parent_class = 0;

/************* Implementation of internal functions ***************/
static void
__im_object_queue_initialize (IMPointer queue)
{
    ((IMObjectQueue*)queue)->head = 0;
    ((IMObjectQueue*)queue)->tail = 0;
    ((IMObjectQueue*)queue)->count= 0;
}

static void
__im_object_queue_finalize (IMPointer queue)
{
    IMObjectQueue *p = IM_OBJECT_QUEUE (queue);
    IMList        *ptr;

    _im_assert (p != 0);

    for (ptr = p->head; ptr; ptr = im_list_next(ptr))
        if (ptr->data)
            im_object_unref (ptr->data);

    if (p->head)
        im_list_free (p->head);

    p->head = 0;
    p->tail = 0;
    p->count = 0;
}

static void
__im_object_queue_copy (IMPointer dest, IMConstPointer src)
{
    IMObjectQueue       *dp = IM_OBJECT_QUEUE (dest);
    const IMObjectQueue *sp = IM_CONST_OBJECT_QUEUE (src);
    IMList              *ptr;

    _im_assert (dp != 0 && sp != 0);
    __im_object_queue_finalize (dest);

    for (ptr = sp->tail; ptr; ptr = im_list_previous (ptr)) {
        if (ptr->data) {
            IMPointer obj = im_object_clone (ptr->data);
            if (obj) {
                im_object_ref (obj);
                dp->head = im_list_prepend (dp->head, obj);
                if (!dp->tail) dp->tail = dp->head;
                dp->count++;
            }
        }
    }

    /* Call copy method of parent class */
    __im_object_queue_parent_class->copy (dest, src);
}

static IMBool
__im_object_queue_serialize (IMConstPointer obj, IMPointer stream)
{
    const IMObjectQueue *qp = IM_CONST_OBJECT_QUEUE (obj);
    IMStream            *sp = IM_STREAM (stream);
    IMList              *ptr;

    _im_assert (qp != 0 && sp != 0);

    /* Call parent serialize method */
    if (!__im_object_queue_parent_class->serialize (obj, stream))
        return FALSE;
    if (!im_stream_put_uint32 (sp, qp->count))
        return FALSE;

    for (ptr = qp->head; ptr; ptr = im_list_next (ptr)) {
        IMBool ret;
        if (ptr->data)
            ret = im_stream_put_object (sp, ptr->data);
        else
            ret = im_stream_put_void (sp);
        if (!ret) return FALSE;
    }
    return TRUE;
}

static IMBool
__im_object_queue_deserialize (IMPointer obj, IMPointer stream)
{
    IMObjectQueue   *qp = IM_OBJECT_QUEUE (obj);
    IMStream        *sp = IM_STREAM (stream);
    IMPointer        val;
    IMType           type;
    IMSize           i;
    IMUInt32         n = 0;

    _im_assert (qp != 0 && sp != 0);

    /* Call parent deserialize method */
    if (!__im_object_queue_parent_class->deserialize (obj, stream))
        return FALSE;

    if (im_stream_get_data_type (sp) != IM_TYPE_UINT32 || !im_stream_get_uint32 (sp, &n)) 
        return FALSE;

    __im_object_queue_finalize (qp);

    for (i = 0; i < n; ++i) {
        type = im_stream_get_data_type (sp);
        if (type != IM_TYPE_VOID) {
            if (!IM_TYPE_IS_OBJECT(type))
                return FALSE;
            /* Newly created object should be owned by the IMObjectQueue object.*/
            val = im_object_ref (im_object_new (type));
            if (!val) {
                _im_warn ("Failed to create object for type id %d, name %s\n", type, im_type_get_name (type));
                return FALSE;
            }
            if (!im_stream_get_object (sp, val)) {
                im_object_unref (val);
                return FALSE;
            }
        } else {
            val = 0;
        }
        qp->head = im_list_prepend (qp->head, val);
        if (!qp->tail)
            qp->tail = qp->head;
        qp->count++;
    }

    if (qp->head) {
        qp->tail = qp->head;
        qp->head = im_list_reverse (qp->head);
    }
    return TRUE;
}

static void
__im_object_queue_class_init (IMPointer klass)
{
    IMObjectClass *cp = (IMObjectClass *)klass;

    cp->copy        = __im_object_queue_copy;
    cp->serialize   = __im_object_queue_serialize;
    cp->deserialize = __im_object_queue_deserialize;

    __im_object_queue_parent_class = im_object_class_get_parent (cp);
}

/************* Implementation of public functions ***************/
void
__im_type_register_object_queue ()
{
    static IMTypeInfo im_object_queue_type_info = 
    {
        sizeof (IMObjectQueueClass), /**< class_size >**/
        __im_object_queue_class_init,/**< class_init >**/
        0,                           /**< class_finalize >**/

        sizeof (IMObjectQueue),      /**< instance_size >**/
        __im_object_queue_initialize,/**< instance_init >**/
        __im_object_queue_finalize,  /**< instance_finalize >**/
    };

    __im_type_register_builtin_class (IM_TYPE_OBJECT,
                                      IM_TYPE_OBJECT_QUEUE,
                                      "ObjectQueue",
                                      &im_object_queue_type_info,
                                      TRUE);
}

IMObjectQueue*
im_object_queue_new ()
{
    IMObjectQueue *q = (IMObjectQueue *) im_object_new (IM_TYPE_OBJECT_QUEUE);
    _im_assert (q);
    return q;
}

void
im_object_queue_clear (IMObjectQueue *queue)
{
    _im_return_if_fail (IM_IS_OBJECT_QUEUE (queue));
    __im_object_queue_finalize (queue);
}

IMSize
im_object_queue_length (const IMObjectQueue *queue)
{
    _im_return_val_if_fail (IM_IS_OBJECT_QUEUE (queue), 0);
    return queue->count;
}

IMPointer
im_object_queue_front (const IMObjectQueue *queue)
{
    _im_return_val_if_fail (IM_IS_OBJECT_QUEUE (queue), 0);
    return queue->head ? queue->head->data : 0;
}

IMPointer
im_object_queue_back (const IMObjectQueue *queue)
{
    _im_return_val_if_fail (IM_IS_OBJECT_QUEUE (queue), 0);
    return queue->tail ? queue->tail->data : 0;
}

void
im_object_queue_push_front (IMObjectQueue *queue, IMPointer object)
{
    _im_return_if_fail (IM_IS_OBJECT_QUEUE (queue));
    _im_return_if_fail (IM_IS_OBJECT (object));

    im_object_ref (object);
    queue->head = im_list_prepend (queue->head, object);
    if (!queue->tail)
        queue->tail = queue->head;
    queue->count++;
}

void
im_object_queue_push_back (IMObjectQueue *queue, IMPointer object)
{
    IMList *node;
    _im_return_if_fail (IM_IS_OBJECT_QUEUE (queue));
    _im_return_if_fail (IM_IS_OBJECT (object));

    im_object_ref (object);

    node = im_list_alloc();
    node->data = object;
    node->next = 0;
    if (queue->tail) {
        node->prev = queue->tail;
        queue->tail->next = node;
        queue->tail = node;
    } else {
        queue->head = queue->tail = node;
        node->prev = 0;
    }
    queue->count++;
}

void
im_object_queue_pop_front (IMObjectQueue *queue)
{
    _im_return_if_fail (IM_IS_OBJECT_QUEUE (queue));

    if (queue->head) {
        IMList *node = queue->head;
        if (node->next)
            node->next->prev = 0;
        else
            queue->tail = 0;
        queue->head = node->next;
        queue->count--;

        if (node->data)
            im_object_unref (node->data);

        im_list_free_node (node);
    }
}

void
im_object_queue_pop_back (IMObjectQueue *queue)
{
    _im_return_if_fail (IM_IS_OBJECT_QUEUE (queue));

    if (queue->tail) {
        IMList *node = queue->tail;
        if (node->prev)
            node->prev->next = 0;
        else
            queue->head = 0;
        queue->tail = node->prev;
        queue->count--;

        if (node->data)
            im_object_unref (node->data);

        im_list_free_node (node);
    }
}

/*
vi:ts=4:nowrap:ai:expandtab
*/
